/*
 * Copyright (C) 2008-2010 aki@akjava.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.akjava.lib.android.image;



import java.io.FileNotFoundException;
import java.io.FileOutputStream;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory.Options;
import android.util.Log;

/**
 * 
 * @author aki
 * @version 1.0
 * 画像を処理するためのクラス
 * メソッド名は大いに改善の余地あり
 *
 */
public class ImageUtils {
	public static final String[] imageExtensions={"jpg","png","jpeg","gif"};
	
	private ImageUtils(){}

	/**
	 * 画像を指定の縦幅・横幅の中に収まるようにアスペクト比を維持したままリサイズします。
	 * 横幅か縦幅が指定と同じなります。
	 * 
	 * @param baseImage　元画像
	 * @param width　横幅
	 * @param height　縦幅
	 * @return  横幅か縦幅が指定と同じサイズの画像
	 */
	public static Bitmap fitImageNoMargin(Bitmap baseImage,int width,int height){
		
		Point pt=calculateFitImage(baseImage,width,height,null);//TODO gc free
		Bitmap resizedBitmap = Bitmap.createScaledBitmap(baseImage,
				pt.x, pt.y, true); 
		return resizedBitmap;
	}
	
	/**
	 * なるべく指定の幅に近いサイズにリサイズして画像を読み込んで返します。
	 * 指定より小さくなることはりません。
	 * テクスチャーサイズに合わせる時などに使います
	 * 
	 * @param path
	 * @param width
	 * @param config
	 * @return
	 */
	public static Bitmap sampleSizeOpenBitmapByWidth(String path,int width,Bitmap.Config config){
		Point size=parseJpegSize(path, null);
		int rw=size.x/width;
		
		int sampleSize=Math.max(rw, 1);
		Bitmap bitmap=sampleSizeOpenBitmap(path, sampleSize);
		return bitmap;
	}
	
/**
 * 画像を読み込んで、背景黒の横幅・縦幅の枠に合わせます。
 * 
 * 
 * @param path
 * @param width
 * @param height
 * @param config nullの場合はBitmap.Config.RGB_565になります。
 * @return　指定の横幅・縦幅サイズの画像が返ります。隙間の背景色は黒です
 */
	public static Bitmap fitImage(String path,int width,int height,Bitmap.Config config){
		//TODO set bgcolor
		Point size=parseJpegSize(path, null);
		int rw=size.x/width;
		int rh=size.y/height;
		int sampleSize=Math.min(rw, rh);
		sampleSize=Math.max(sampleSize, 1);
		Bitmap bitmap=sampleSizeOpenBitmap(path, sampleSize,config);
		return fitImage(bitmap, width, height, config, true);
	}
	
		
	
	
	/**
	 * 画像をリサイズしてます。
	 * 
	 * @param baseImage
	 * @param width
	 * @param height
	 * @param config nullの場合はBitmap.Config.RGB_565になります。
	 * @param doRecycleBase 途中でbitmapをrecycleするかどうか、true推奨
	 * @return　指定の横幅・縦幅サイズの画像が返ります。隙間の背景色は黒です
	 */
	public static Bitmap fitImage(Bitmap baseImage,int width,int height,Bitmap.Config config,boolean doRecycleBase){
		if(baseImage==null){
			throw new RuntimeException("baseImage is null");
		}
		if(config==null){
			config=Bitmap.Config.RGB_565;
		}
		Point resizedP=calculateFitImage(baseImage, width, height, null);//TODO gc free
		
		
        Bitmap resizedBitmap =  Bitmap.createScaledBitmap(baseImage,
				resizedP.x, resizedP.y, true); 
        
        if(doRecycleBase){//to avoid memory error
        	baseImage.recycle();
        }
        
        Bitmap returnBitmap=Bitmap.createBitmap(width, height, config);
        Canvas canvas=new Canvas(returnBitmap);
        canvas.drawBitmap(resizedBitmap, (width-resizedP.x)/2, (height-resizedP.y)/2,null);
        resizedBitmap.recycle();
        
		return returnBitmap;
	}
	
	/**
	 * 画像が枠に収まるようリサイズする場合のサイズを計算します
	 * 
	 * @param baseImage
	 * @param width
	 * @param height
	 * @param receiver
	 * @return
	 */
	public static Point calculateFitImage(Bitmap baseImage,int width,int height,Point receiver){
		if(baseImage==null){
			throw new RuntimeException("baseImage is null");
		}
		if(receiver==null){
			receiver=new Point();
		}
		int dw=width;
		int dh=height;
		
		
		if(dw!=0 && dh!=0 ){
			double waspect=(double)dw/baseImage.getWidth();
			double haspect=(double)dh/baseImage.getHeight();
			if(waspect>haspect){//fit h
				dw=(int) (baseImage.getWidth()*haspect);
				
			}else{
				dh=(int)(baseImage.getHeight()*waspect);
			}
		}
		receiver.x=dw;
		receiver.y=dh;
		return receiver;
	}
	/**
	 *  テクスチャー画像に変換します。素材画像はrecyleしません。
	 *  
	 * @param baseImage
	 * @param width
	 * @param height
	 * @return
	 */
	public static Bitmap toTextureImage(Bitmap baseImage,int width,int height){
		return toTextureImage(baseImage, width, height,false);
	}
	
	/**
	 * PNG 画像として保存します
	 * 
	 * @param bitmap
	 * @param output
	 * @return
	 * @throws FileNotFoundException
	 */
	public static boolean writeAsPng(Bitmap bitmap,String output) throws FileNotFoundException{
		return bitmap.compress(Bitmap.CompressFormat.PNG, 1, new FileOutputStream(output));
	}
	
	/**
	 * テクスチャー画像に変換します。
	 * 
	 * @param baseImage
	 * @param width must be divided 2 (like 256 or 512)
	 * @param height must be divided 2 (like 256 or 512)
	 * @return
	 */
	public static Bitmap toTextureImage(Bitmap baseImage,int width,int height,boolean recycle){
	
		int owidth = baseImage.getWidth(); 
        int oheight = baseImage.getHeight(); 
       
        
        // calculate the scale - in this case = 0.4f 
        float scaleWidth = ((float) width) / owidth; 
        float scaleHeight = ((float) height) / oheight; 
		
      
		Matrix matrix = new Matrix(); 
		  
        // resize the bit map 
        matrix.postScale(scaleWidth, -scaleHeight); 
        // rotate the Bitmap 
        //matrix.postRotate(-180); 
        
        Bitmap resizedBitmap = Bitmap.createBitmap(baseImage, 0, 0, 
        		owidth, oheight, matrix, true); 
        if(recycle){
        	baseImage.recycle();
        }
        Log.i("myapp","resized:"+resizedBitmap.getWidth()+"x"+resizedBitmap.getHeight());
		
		return resizedBitmap;
	}
	
	/**
	 * 左右どちらかに回転したテクスチャー画像に変換します
	 * 
	 * @param baseImage
	 * @param width
	 * @param height
	 * @param rotateRight
	 * @return
	 */
	public static Bitmap toRotateTextureImage(Bitmap baseImage,int width,int height,boolean rotateRight){
		
		int owidth = baseImage.getWidth(); 
        int oheight = baseImage.getHeight(); 
       
        
        // calculate the scale - in this case = 0.4f 
        float scaleWidth = ((float) width) / owidth; 
        float scaleHeight = ((float) height) / oheight; 
		
      
		Matrix matrix = new Matrix(); 
		  
        // resize the bit map 
        matrix.postScale(scaleWidth, -scaleHeight); 
        // rotate the Bitmap 
        if(rotateRight){
        matrix.postRotate(90); 
        }else{
        matrix.postRotate(-90); 	
        }
        
        Bitmap resizedBitmap = Bitmap.createBitmap(baseImage, 0, 0, 
        		owidth, oheight, matrix, true); 
        
        Log.i("myapp","resized:"+resizedBitmap.getWidth()+"x"+resizedBitmap.getHeight());
		
		return resizedBitmap;
	}
	
	/**
	 * 最後に読み込んだ画像のsampleSize、オリジナルのwidthとheightを取り出すのに役立つ
	 */
	public static int lastSampleSize=1;
	//public static BitmapFactory.Options bitmapOption;//cancel用だけどスレッドdenger
	
	/**
	 *  なるべく大きなサイズで画像を読み込みます。
	 *  メモリエラーで読み込めない場合は、サンプルサイズを大きくして再度読み込みます。
	 *  
	 * @param path
	 * @param startSize
	 * @param config
	 * @return
	 */
	public static Bitmap sampleSizeOpenBitmap(String path, int startSize,
			Config config) {
		BitmapFactory.Options bitmapOption = new BitmapFactory.Options();
		bitmapOption.inPreferredConfig=config;
		return sampleSizeOpenBitmap(path, startSize,bitmapOption);
	}
	
	/**
	 *  なるべく大きなサイズで画像を読み込みます。
	 *  メモリエラーで読み込めない場合は、サンプルサイズを大きくして再度読み込みます。
	 *  
	 * @param path
	 * @param startSize
	 * @param bitmapOption
	 * @return
	 */
	public static Bitmap sampleSizeOpenBitmap(String path, int startSize,
			Options bitmapOption) {		
		int inSampleSize=startSize;
		Bitmap bitmap=null;
		for(int i=0;i<10;i++){
			
		try{
			bitmapOption.inSampleSize=inSampleSize;
			bitmap=BitmapFactory.decodeFile(path,bitmapOption);
			//Log.i("imageutils","decoded bitmap:"+bitmapOption.inSampleSize);
			lastSampleSize=inSampleSize;
			if(bitmap==null){
				System.gc();//ready for next
			}
		}catch(Error e){
			Log.i("imageutils","faild load:"+inSampleSize+" "+path);
		}
		if(bitmap!=null || bitmapOption.mCancel){
			break;
		}else{
			//inSampleSize*=2;
			inSampleSize+=1;
		}
	}
		return bitmap;
	}
	
	/**
	 * 指定の枠に収まるように、画像を読み込みます。
	 * 
	 * @param path
	 * @param width
	 * @param height
	 * @param config
	 * @return
	 */
	public static Bitmap sampleSizeOpenBitmap(String path,int width,int height,Config config){
		Point size=parseJpegSize(path, null);
		int rw=size.x/width;
		int rh=size.y/height;
		int sampleSize=Math.min(rw, rh);
		sampleSize=Math.max(sampleSize, 1);
		return sampleSizeOpenBitmap(path, sampleSize,config);
	}
	
	/**
	 * MemoryErrorでとまらないように開く、Content://の画像は開けない
	 * おそらく RGB565で読み込む
	 * @see MediaImageUtils#loadImageFileOrUri(android.content.ContentResolver, String)
	 * @param path
	 * @param startSize
	 * @return
	 */
	
	public static Bitmap sampleSizeOpenBitmap(String path,int startSize){
		return sampleSizeOpenBitmap(path, startSize,(Config) null);
	}
	
	/**
	 * 拡張子から画像ファイルかどうか判断します
	 * @param name
	 * @return
	 */
	public static boolean isImageFile(String name){
		for (int i = 0; i < imageExtensions.length; i++) {
		if(name.toLowerCase().endsWith("."+imageExtensions[i]))	{
			return true;
		}
		}
		return false;
	}
	
	/**
	 * 画像を切り抜きます
	 * 
	 * @param bitmap
	 * @param rect
	 * @return
	 */
	public static Bitmap cropBitmap(Bitmap bitmap,Rect rect){
		int w=rect.right-rect.left;
		int h=rect.bottom-rect.top;
		Bitmap ret=Bitmap.createBitmap(w, h, bitmap.getConfig());
		Canvas canvas=new Canvas(ret);
		canvas.drawBitmap(bitmap, -rect.left, -rect.top, null);
		return ret;
	}
	
	/**
	 * JPEG画像のサイズを調べます
	 * 
	 * @param path
	 * @param receiver
	 * @return
	 */
	public static Point parseJpegSize(String path,Point receiver){
		if(receiver==null){
			receiver=new Point();
		}
		
		Options option=new BitmapFactory.Options();
		option.inJustDecodeBounds=true;
		BitmapFactory.decodeFile(path,option);
		receiver.x=option.outWidth;
		receiver.y=option.outHeight;
		return receiver;
	}
	
}
